from decimal import Decimal

from django.shortcuts import render
from utils.views import LoginRequiredJsonMixin
from django.http import HttpResponseBadRequest, JsonResponse
from django_redis import get_redis_connection
import json
from apps.users.models import Address
from django.views import View
from apps.goods.models import SKU
# Create your views here.
from apps.orders.models import OrderInfo, OrderGoods
from django.utils import timezone
from django.db import transaction

class OrderCommitView(LoginRequiredJsonMixin, View):
    def post(self, request):
        '''生成订单数据'''
        # 拿到前端传来的收货地址和支付方法
        data = json.loads(request.body.decode())
        addr_id = data.get('address_id')  # 地址编号
        pay_method = data.get('pay_method')  # 支付方式

        # 效验传来的数据
        try:
            address = Address.objects.get(id=addr_id)  # 拿到地址对象
        except Address.DoesNotExist:
            return HttpResponseBadRequest('没有地址')

        if pay_method not in [OrderInfo.PAY_METHODS_ENUM["CASH"], OrderInfo.PAY_METHODS_ENUM["ALIPAY"]]:
            return HttpResponseBadRequest('数据不合法')


        # 获取登录用户并生成订单号
        user = request.user
        order_id = timezone.localtime().strftime('%Y%m%d%H%M%S') + ('%09d' % user.id)  # 订单编号

        with transaction.atomic():
            save_id = transaction.savepoint()
            # 保存订单的基础信息
            order = OrderInfo.objects.create(
                order_id=order_id,
                user=user,
                address=address,
                total_count=0,
                total_amount=Decimal('0'),
                freight=Decimal('10.00'),
                pay_method=pay_method,
                status=OrderInfo.ORDER_STATUS_ENUM['UNPAID'] if pay_method == OrderInfo.PAY_METHODS_ENUM['ALIPAY'] else
                OrderInfo.ORDER_STATUS_ENUM['UNSEND']
            )

            # 从redis中读取勾选商品id和count
            redis_conn = get_redis_connection('carts')
            sku_id_count = redis_conn.hgetall('cartsb_%s' % user.id)
            sku_selected = redis_conn.smembers('selectedb_%s' % user.id)

            # 将数据转成int
            sku_ids_counts = {}
            for sku_id in sku_selected:
                sku_ids_counts[int(sku_id)] = int(sku_id_count[sku_id])

            # 遍历商品id检查库存是否充足
            for sku_id, count in sku_ids_counts.items():
                while True:  # 使用乐观锁循环比对实际库存与程序认为的库存
                    sku = SKU.objects.get(id=sku_id)
                    # 读取原始库存和销量
                    origin_stock = sku.stock
                    origin_sales = sku.sales
                    if count > sku.stock:
                        transaction.savepoint_rollback(save_id)
                        return HttpResponseBadRequest('库存不足')



                    # 对库存进行计算
                    # sku.stock -= count
                    # sku.sales += count
                    # sku.save()
                    #
                    # 程序认为的库存与销量
                    new_stock = origin_stock - count
                    new_sales = origin_sales + count


                    # 在这里我们把程序认为的库存与销量和数据库比对如果相同就继续不同就重新循环
                    resute = SKU.objects.filter(id=sku_id, stock=origin_stock).update(
                        stock=new_stock,
                        sales=new_sales,
                    )
                    if resute == 0:
                        continue

                    # 保存商品信息
                    try:
                        OrderGoods.objects.create(
                            order=order,
                            sku=sku,
                            count=count,
                            price=sku.price
                        )
                    except OrderGoods.DoesNotExist:
                        return HttpResponseBadRequest('保存失败')

                    # updata商品信息中的总金额和商品总数
                    order.total_count += count
                    order.total_amount += (sku.price * count)
                    # 程序执行到这里说明成功下单
                    break

            # 总价加邮费并保存
            order.total_amount += order.freight
            order.save()
            transaction.savepoint_commit(save_id)
        # 删除redis中勾选的商品
        redis_conn.hdel('cartsb_%s' % user.id, *sku_selected)
        redis_conn.srem('selectedb_%s' % user.id, *sku_selected)

        # 返回数据
        return JsonResponse({'code': 0, 'errmsg': '下单成功', 'order_id': order.order_id})


# ==============================================================================================
# 返回订单数据
class OrderSettlementView(LoginRequiredJsonMixin, View):
    def get(self, request):
        '''返回订单数据'''
        # 拿到数据用户的所有地址信息
        user = request.user

        address = Address.objects.filter(user=user)  # 用户的所有地址信息


        # 从redis中取出勾选的商品id与count值
        redis_conn = get_redis_connection('carts')

        sku_id_count = redis_conn.hgetall('cartsb_%s' % user.id)
        sku_id_selected = redis_conn.smembers('selectedb_%s' % user.id)


        # 将redis中取出的数据由byte转成int
        sku_ids_counts = {}  # 商品id与count的int类型
        for sku_id in sku_id_selected:
            sku_ids_counts[int(sku_id)] = int(sku_id_count[sku_id])

        # 用商品id查询商品信息
        sku_list = []  # 商品列表返回前端
        skus = SKU.objects.filter(id__in=sku_id_selected)
        for sku in skus:

            sku_list.append({
                'id': sku.id,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'count': sku_ids_counts[sku.id],
                'price': sku.price
            })

        # 用地址信息生成地址数据
        address_list = []
        for addr in address:
            address_list.append({
                'id': addr.id,
                'province': addr.province.name,
                'city': addr.city.name,
                'district': addr.district.name,
                'place': addr.place,
                'receiver': addr.receiver,
                'mobile': addr.mobile
            })

        # 创建运费
        freight = 10

        # 渲染界面
        context = {
            'addresses': address_list,
            'skus': sku_list,
            'freight': freight,
        }


        # 将商品数据地址数据运费返回
        return JsonResponse({
            'code': 0,
            'errmsg': 'ok',
            'context': context
        })



